add support for caching (DCNE-680)#148
add support for caching (DCNE-680)#148juchowan wants to merge 6 commits intociscoecosystem:masterfrom
Conversation
client/client.go
Outdated
| } | ||
|
|
||
| // deepCloneContainer creates a true deep copy of a Container to prevent shared data races | ||
| func (c *Client) deepCloneContainer(original *container.Container) (*container.Container, error) { |
There was a problem hiding this comment.
why expose this in client and not in gabs.go as a method/function on the container itself?
client/client.go
Outdated
| } | ||
|
|
||
| // GetSchemaWithCache retrieves schema with caching support | ||
| func (c *Client) GetSchemaWithCache(schemaId string) (*container.Container, error) { |
There was a problem hiding this comment.
Should we make the cache mechanism generic to allow for anything to be cached with some sort of cache-id?
There was a problem hiding this comment.
we could but im just not sure if it makes sense, i dont see any other object in ndo that could grow as much as schema to need caching, the new tenant templates have pretty limited configuration options so we wouldnt gain much from caching them
There was a problem hiding this comment.
I would argue two point to this:
- schema originally was not this big but it grew overtime, in some template structures like l3out templates there are already large configurations possible
- from a consistency point of view I think the caching should behave similar for every GET request
client/client.go
Outdated
| return word | ||
| } | ||
|
|
||
| type ThreadSafeCache struct { |
There was a problem hiding this comment.
why add this too client file instead of as separate cache file?
do we have a non thread safe cache? should we just name it cache and allow have thread-safe implementation for the methods/functions?
client/client.go
Outdated
| } | ||
|
|
||
| // DeletePattern removes all items from the cache matching the pattern. | ||
| func (c *ThreadSafeCache) DeletePattern(pattern string) { |
There was a problem hiding this comment.
do we also require a delete all or clear cache method/function?
There was a problem hiding this comment.
there is InvalidateAllSchemaCache that can be used to clear all cache in case it's needed - will change that to Clear to have it for all possible cache. We also have InvalidateSchemaCache that is used after every create/update operation on schema resources
not sure there is need for anything more, closing the terraform sessions should clear cache on its own
client/client.go
Outdated
| } | ||
|
|
||
| // Set adds or updates an item in the cache. | ||
| func (c *ThreadSafeCache) Set(key string, value interface{}) { |
There was a problem hiding this comment.
the c is kind of confusing me with the c used for client, shall we make this more specific?
client/client.go
Outdated
| items map[string]interface{} | ||
| hits int64 | ||
| misses int64 | ||
| invalidations int64 |
There was a problem hiding this comment.
What is your exact goal of this statistics part? should we also include statistics per cache item?
If we want to expose statistics I think we should create a statistics struct which is part of each item, with the necessary data.
type ThreadSafeCache struct {
mu sync.RWMutex
items map[string]CacheItem
}
type CacheItem struct {
item interface{}
hits int64
misses int64
invalidations int64
}
Perhaps even further where the statistics are also set in its own type.
Then have a cache statistics method/function that would provide a statistics report based on preference of statistics you would like to represent.
client/client.go
Outdated
| hits, misses, invalidations, hitRatio := c.Cache.GetStats() | ||
| log.Printf("[DEBUG] SCHEMA_CACHE_INVALIDATED for %s | Stats: Hits=%d, Misses=%d, Invalidations=%d, HitRatio=%.1f%%", | ||
| schemaId, hits, misses, invalidations, hitRatio) |
There was a problem hiding this comment.
why not leverage the LogCacheStats function, or some other wrapper function to prevent all the copied code?
README.md
Outdated
|
|
||
| ```golang | ||
| // Fetch schema with caching support (automatically handles cache hits/misses) | ||
| schema, err := msoClient.GetSchemaWithCache("schema-id") |
There was a problem hiding this comment.
why not adjust the current GetViaURL, so that when caching is enabled it leverage caching for everything?
There was a problem hiding this comment.
for now i wanted to enabled it on selected resources only that we know are problematic to see the behaviour
There was a problem hiding this comment.
I personally think we should have consistent behaviour on all the resources, but this is not a resource (schema) discussion in my opinion but a generic caching discussion. In this case I would say we should have something like a GetViaURLWithCache function that can be used for schema ( but also for other endpoints ) where it would access the caching mechanism.
In the provider we can then gradually change those resources.
README.md
Outdated
| // Invalidate a specific schema from cache (e.g., after updates) | ||
| msoClient.InvalidateSchemaCache("schema-id") | ||
|
|
||
| // Clear all cached schemas (useful for bulk operations or cleanup) | ||
| msoClient.InvalidateAllSchemaCache() | ||
|
|
||
| // Get cache statistics for monitoring | ||
| hits, misses, invalidations, hitRatio := msoClient.GetCacheStats() | ||
|
|
||
| // Log current cache statistics | ||
| msoClient.LogCacheStats() |
There was a problem hiding this comment.
I think these functions/methods should be part of a cache struct and not the client
| const ( | ||
| KB = 1024 | ||
| MB = 1024 * 1024 | ||
| ) |
There was a problem hiding this comment.
what are these constants? could you provide come comment/context for them?
| } | ||
| } | ||
|
|
||
| // Set adds or updates an item in the cache with per-item size tracking. |
There was a problem hiding this comment.
why are you introducing per item size tracking? is there a use case for this?
| itemSize = int64(len(jsonBytes)) | ||
| } else { | ||
| // Fallback for non-byte values | ||
| itemSize = 1024 // Estimate 1KB for unknown types |
There was a problem hiding this comment.
why not use the defined constant for this?
| func CacheEnabled(enabled bool) Option { | ||
| return func(client *Client) { | ||
| client.cacheEnabled = enabled | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
is this required? can't we init the client with a cache when we provide that option? this way when a cache exist on the client it is used and else it is not
| } | ||
|
|
||
| // detectURLResourceType detects resource type from URL for better logging and monitoring | ||
| func (c *Client) detectURLResourceType(url string) string { |
There was a problem hiding this comment.
what is the purpose of this? I see a lot of logging / statistics things being added when I think we should first align on the caching mechanism itself. Is the url itself not clear enough in logging?
| } | ||
|
|
||
| // Get gets and clones an item with per-item statistics tracking | ||
| func (cache *Cache) Get(key string, cloneFunc func(interface{}) (interface{}, error)) (interface{}, bool, error) { |
There was a problem hiding this comment.
why do we need clonefunc? not sure i really see the benefit of passing in a function like this, why not handle this cloning if required inside of the get itself?
|
|
||
| cacheKey := url | ||
|
|
||
| passthroughFunc := func(item interface{}) (interface{}, error) { |
There was a problem hiding this comment.
i do not understand what the benefit of the passthroughFunc is giving, could you please elaborate the intent of this?
| c.Cache.LogEvent(resourceType+"_CACHED", url, true) | ||
| } else { | ||
| // Data was cached by another goroutine while we were fetching | ||
| log.Printf("[DEBUG] %s already cached by another goroutine for %s", resourceType, url) |
There was a problem hiding this comment.
should we also then check that the content of the cached item is the same? what happens when they are different?
| // Fetch data with caching support (automatically handles cache hits/misses) | ||
| data, err := msoClient.GetViaURLWithCache("/api/v1/schemas/schema-id") | ||
|
|
||
| // Invalidate a specific URL from cache (e.g., after updates) | ||
| msoClient.InvalidateURLCache("/api/v1/schemas/schema-id") | ||
|
|
||
| // Clear all cached items (useful for bulk operations or cleanup) | ||
| msoClient.ClearCache() |
There was a problem hiding this comment.
how do you propose to get the changes into the provider? like after this caching change has been added to the client, what is required in the provider to change in order to support it?
|
|
||
| } | ||
|
|
||
| func (c *Client) GetViaURLWithCache(url string) (*container.Container, error) { |
There was a problem hiding this comment.
do we need this a a separate function? why not leverage the GetViaURL and detect if there is caching enabled there, this way none of the current provider code needs to be changed, you would keep functionality as is and then when somebody enables caching it would run that cache function.
No description provided.